/**
 * title: FeatureConceptDAO.java
 * description: Program that provides abstract interface to the database with the
 *              help of the FeatureConcept model, which encapsulates feature concept related items.
 * date: December 28 2013
 * id: 2540434
 * course: COMP 495 (Project-Server)
 * @author Mohamed B Thaha
 * @version 1.0
 * @copyright 2013 Mohamed B Thaha
 */

/**
 * DOCUMENTATION...
 */

/**                                                                               
 *
 *<H1>FeatureConceptDAO</H1>
 *
 *<H3>Purpose and Description</H3>
 *
 *<P>
 * An application that allows the job of the DAO (data access object) to provide 
 * an abstract interface to the database.
 *</P>
 *<P>
 * The DAOs main purpose is to perform CRUD operations on the database, which basically 
 * means create, retrieve, update, and delete data by virtue of a database connection or 
 * JDBC API driver.
 *</P>
 *<P>
 * This program requires the Sun Java SDK version 1.7 or better.
 *</P>
 *                                                                              
 *<DL>
 *<DT> Compiling and running instructions</DT>
 *<DT> Assuming SDK 1.7 (or later) and the CLASSPATH are set up properly.</DT>
 *<DT> Assuming the use of Eclipse IDE (Kepler) 4.3.1.</DT>
 *<DT> Change to the directory containing the source code.</DT>
 *<DD> Compile:    FeatureConceptDAO.java</DD>
 *<DD> Run:        FeatureConceptDAO.java</DD>
 *<DD> Document:   Generate Javadoc FeatureConceptDAO</DD>
 *</DL>
 */

/**
 *
 * <H3>Classes</H3>
 *
 *<P>
 * public class FeatureConceptDAO {<BR>
 * This is the main public class for this application. It contains all the
 * methods required to perform CRUD operations.
 *</P>
 *
 * <H3>FeatureConceptDAO Methods</H3>
 *
 *<P>
   public static boolean create(long device_id, String newFeature) throws Exception<BR>
   This method returns either a true value indicating that a feature name has been
   registered into the database or a false value indicating that the insertion
   has failed.
 *</P>
 * 
 *<P>
   public static FeatureConcept[] select(Hashtable<String, Object> device_filters) throws Exception {<BR>
   This method returns an array of FeatureConcepts if such exists. The method also coincides
   with the Device select method which retrieves an array of devices.
 *</P>
 *
 *<P>
   public static Boolean update(long id, String currentFeatureName, Hashtable<String, Object> args) throws Exception {<BR>
   This method returns either a true value indicating that the update was a success 
   or a false value indicating that the update has failed.
 *</P> 
 *
 *<P>
   public static boolean delete(long device_id) throws Exception {<BR>
   This method returns either a true value indicating that all registers 
   with the specified device id has been deleted or a false value indicating 
   that the operation has failed.
 *</P>
 *
 *<P>
   public static boolean delete(long device_id, String oneFeature) throws Exception {<BR>
   This method returns either a true value indicating a single feature name corresponding 
   to the specified device id has been deleted or a false value indicating that the 
   operation has failed.
 *</P> 
 * 
 * <H3>FeatureConceptDAO Instance Variables (Tags - Constants)</H3>
 *
 *<P>
 * private static final String FEATURE_NAME = "feature_name";
 * private static final String DEVICE_ID = "device_id";
 *</P>
 */

/**
 * CODE...
 */

package athabasca.ca.dao;
/** Java core packages */
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;

import athabasca.ca.model.FeatureConcept;
import athabasca.ca.util.*;
/** primary (public) class for FeatureConceptDAO */
public class FeatureConceptDAO {
	public static final String FEATURE_NAME = "feature_name";
	public static final String DEVICE_ID = "device_id";
	
	/**
	 *  Method's purpose is to insert a new feature name into the database
	 * @param device_id
	 * device identification number
	 * @param newFeature
	 * a new device feature
	 * @return
	 * boolean
	 * @throws Exception
	 */
	public static boolean create(long device_id, FeatureConcept newFeature) throws Exception {
		Connection conn = null;
		PreparedStatement query = null;
		boolean isTrue = false;
		
		try {
			// A 'safeguard' that makes sure the new feature name is not empty 
			// otherwise do not proceed to execute the rest of the code.
			if ((newFeature == null) || (device_id <= 0))
				return false;
			else {
			
			conn = postgreSQL.postgreSQLConn();
			
			// This query will only insert data into the device_features table.
			query = conn.prepareStatement("INSERT INTO device_features " + 
					"VALUES (?, ?)");
			query.setObject(1, newFeature.getName());
            query.setObject(2, device_id);
        	int insertFeature = query.executeUpdate();
			query.close();
			
			// We must make sure that data was inserted in the device_features
    		// table.
			if (insertFeature > 0) {
				isTrue = true;
			}
			}
			
		} catch (ArrayIndexOutOfBoundsException e) {
        	System.err.println ( e.getMessage( ) );
            e.printStackTrace ( );  
            isTrue = false;
        } catch (NullPointerException e) {
        	System.err.println ( e.getMessage( ) );
        	e.printStackTrace ( );
        	isTrue = false;
        } catch (SQLException sqlError) {
        	System.err.println ( sqlError.getMessage( ) );
        	sqlError.printStackTrace ( );
        	isTrue = false;
        } catch (Exception e) {
        	System.err.println ( e.getMessage( ) );
        	e.printStackTrace();
        	isTrue = false;
        } finally {
        	if (conn != null) conn.close();
        }
		
		return isTrue;
	}

	/**
	 *  Method's purpose is to retrieve an array of selected FeatureConcepts
	 * @param device_filters
	 * hash table
	 * @return
	 * FeatureConcept[]
	 * @throws Exception
	 */
	public static FeatureConcept[] select(Hashtable<String, Object> device_filters) throws Exception {
		PreparedStatement query = null;
		Connection conn = null;

		ArrayList<FeatureConcept> availableFeaturesList = new ArrayList<FeatureConcept>();
		
		try {
			// If the filters contained in the hash table are empty, return a null value 
			// and do not proceed to execute the rest of the code.
			if (device_filters.isEmpty())
				return null;
			else {
			
			FeatureConcept availableFeature;
			
			String sql = "";
			String key = "";
			String statement = "";
			String statement2 = "";
			
			Enumeration<String> keys = device_filters.keys( );
			
			// Build the 'statement' on the fly.
			while( keys.hasMoreElements( ) ) {
				key = keys.nextElement( );
				
				statement2 = "device_features.";
				
				if (key != DEVICE_ID)
					statement = statement + statement2 + key + " = '" + device_filters.get(key) + "'";
				else
					statement = statement + statement2 + key + " = " + device_filters.get(key);
				
				if (keys.hasMoreElements())
					statement = statement + " AND ";
			}
			
			sql = ("SELECT feature_name" +
					" FROM device_features" +
					" WHERE " + statement);
			
			conn = postgreSQL.postgreSQLConn();
			
			// This query selects all data from the device_features table.
			query = conn.prepareStatement(sql);
			
			ResultSet results = query.executeQuery();
			
			// If the results returned from the database was empty, return a null value 
			// and there is not reason to continue to execute the rest of the code.
			if (results.wasNull())
				return null;
			else 
			{
	        
				while (results.next()) 
				{
					availableFeature = new FeatureConcept();
					availableFeature.setName(results.getString(FEATURE_NAME));
					
					availableFeaturesList.add(availableFeature);
				}
			}
			
			query.close();
			}
			
		} catch (ArrayIndexOutOfBoundsException e) {
        	System.err.println ( e.getMessage( ) );
            e.printStackTrace ( );     
        } catch (NullPointerException e) {
        	System.err.println ( e.getMessage( ) );
        	e.printStackTrace ( );
        } catch (SQLException sqlError) {
        	System.err.println ( sqlError.getMessage( ) );
        	sqlError.printStackTrace ( );
        } catch (Exception e) {
        	System.err.println ( e.getMessage( ) );
        	e.printStackTrace();
        } finally {
        	if (conn != null) conn.close();
        }
		
		FeatureConcept[] list = (FeatureConcept[]) availableFeaturesList.toArray(new FeatureConcept[availableFeaturesList.size()]);
		
		if (list.length > 0)
	    	return list;
	    else
	    	return null;
	}

	/** 
	 * Method's purpose is to update feature concept related information based on the provided hash 
	 * table filters
	 * @param id
	 * device identification number
	 * @param currentFeatureName
	 * the current name of the feature to be updated
	 * @param args
	 * hash table
	 * @return
	 * Boolean
	 * @throws Exception
	 */
	/*[RENAN]
	 * Mohamed, I was thinking and I figure out that, for now, the update method for FeatureConceptDAO will no be used.
	 * As the model class has only two attributes which are its table primary keys, we could just let it go without any change to
	 * the database.
	 * For now, this class should return true if the deviceID and currentFeatureName are not null and check if there are any
	 * flags of the up-to-date information (right now, we have none but, in the future, could have some). 
	 * [/RENAN]
	 * */
	public static boolean update(long deviceID, FeatureConcept currentFeatureName, Hashtable<String, Object> args) throws Exception {
		Connection conn = null;
		PreparedStatement query = null;
        boolean isTrue = false;
        
        try {
        	// If the filters contained in the hash table are empty or the current feature name is empty, return a null value 
        	// and do not proceed to execute the rest of the code.
        	if ((currentFeatureName.getName() == null) || (args.isEmpty()) || (deviceID <= 0))
        		return false;
        	else {
        	
        	String sql = "";
        	String key;
        	String f_name;
        	key = f_name  = null;
        	
        	conn = postgreSQL.postgreSQLConn();
        	
        	Enumeration<String> keys = args.keys( );
        	
        	// Build the 'statement' on the fly.
        	while (keys.hasMoreElements()) {
        		key = keys.nextElement();
            	
            	sql = ("UPDATE device_features"
            			+ " SET " + key + " = ?"
                        + " WHERE device_id = " + deviceID
                        + " AND feature_name = " + "'" + currentFeatureName.getName() + "'");
            	
            	switch (key) {
            		case FEATURE_NAME:
            		{
            			f_name = (String) args.get(FEATURE_NAME);
            			query = conn.prepareStatement(sql);
            			query.setObject(1, f_name);
            			int update = query.executeUpdate();
            			query.close();
            			// We must make sure that data was updated in the device_features
                		// table.
            			if (update > 0)
            				isTrue = true;
            			break;
            		}
            		default:
            			isTrue = false;
            			return isTrue;
            	}
        	}
        	}
        } catch (ArrayIndexOutOfBoundsException e) {
        	System.err.println ( e.getMessage( ) );
            e.printStackTrace ( );  
            isTrue = false;
        } catch (NullPointerException e) {
        	System.err.println ( e.getMessage( ) );
        	e.printStackTrace ( );
        	isTrue = false;
        } catch (SQLException sqlError) {
        	System.err.println ( sqlError.getMessage( ) );
        	sqlError.printStackTrace ( );
        	isTrue = false;
        } catch (Exception e) {
        	System.err.println ( e.getMessage( ) );
        	e.printStackTrace();
        	isTrue = false;
        } finally {
        	if (conn != null) conn.close();
        }
        return isTrue;
	}

	/** 
	 * Method's purpose is to delete all feature names tied 
	 * to the specified device id
	 * @param device_id
	 * device identification number
	 * @return
	 * boolean
	 * @throws Exception
	 */
	public static boolean deleteAllFeatures(long device_id) throws Exception {
		Connection conn = null;
		PreparedStatement query = null;
        boolean isTrue = false;
        
        try {
        	if (device_id <= 0)
        		return false;
        	else {
        	conn = postgreSQL.postgreSQLConn();
            	
        	query = conn.prepareStatement("DELETE FROM device_features"
        			+ " WHERE device_id = ?");
            query.setObject(1, device_id);
        	int deleteAll = query.executeUpdate();
            query.close();
            
            // We must make sure that data was deleted from the device_features
    		// table.
            if (deleteAll > 0) {
            	isTrue = true;
            }
        	}
        	
        } catch (ArrayIndexOutOfBoundsException e) {
        	System.err.println ( e.getMessage( ) );
            e.printStackTrace ( );
            isTrue = false;
        } catch (NullPointerException e) {
        	System.err.println ( e.getMessage( ) );
        	e.printStackTrace ( );
        	isTrue = false;
        } catch (SQLException sqlError) {
        	System.err.println ( sqlError.getMessage( ) );
        	sqlError.printStackTrace ( );
        	isTrue = false;
        } catch (Exception e) {
        	System.err.println ( e.getMessage( ) );
        	e.printStackTrace();
        	isTrue = false;
        } finally {
        	if (conn != null) conn.close();
        }
        return isTrue;
	}
	
	/** 
	 * Method's purpose is to delete a single feature name tied to the specified 
	 * device id
	 * @param device_id
	 * device identification number
	 * @param oneFeature
	 * the feature name to be deleted
	 * @return
	 * boolean
	 * @throws Exception
	 */
	public static boolean delete(long device_id, FeatureConcept oneFeature) throws Exception {
		Connection conn = null;
		PreparedStatement query = null;
        boolean isTrue = false;
        
        try {
        	// A 'safeguard' that makes sure the feature name is not empty 
        	// otherwise do not proceed to execute the rest of the code.
        	if ((oneFeature == null) || (device_id <= 0))
        		return false;
        	else {
        	
        	conn = postgreSQL.postgreSQLConn();
            	
        	query = conn.prepareStatement("DELETE FROM device_features"
        			+ " WHERE device_id = ? AND feature_name = ?");
            query.setObject(1, device_id);
            query.setObject(2, oneFeature.getName());
        	int deleteOne = query.executeUpdate();
            query.close();
            
            // We must make sure that data was deleted from the device_features
    		// table.
            if (deleteOne == 1) { // There should be just one feature to be deleted with the pair (device_id, feature_name)
            	isTrue = true;
            }
        	}
        	
        } catch (ArrayIndexOutOfBoundsException e) {
        	System.err.println ( e.getMessage( ) );
            e.printStackTrace ( );    
            isTrue = false;
        } catch (NullPointerException e) {
        	System.err.println ( e.getMessage( ) );
        	e.printStackTrace ( );
        	isTrue = false;
        } catch (SQLException sqlError) {
        	System.err.println ( sqlError.getMessage( ) );
        	sqlError.printStackTrace ( );
        	isTrue = false;
        } catch (Exception e) {
        	System.err.println ( e.getMessage( ) );
        	e.printStackTrace();
        	isTrue = false;
        } finally {
        	if (conn != null) conn.close();
        }
        return isTrue;
	}
} // End of public class FeatureConceptDAO